<template>
  <div id="alone-global-uploader">
      <uploader
        ref="uploader"
        :options="initOptions"
        :fileStatusText="fileStatusText"
        :autoStart="false"
        @file-added="onFileAdded"
        @file-success="onFileSuccess"
        @file-progress="onFileProgress"
        @file-error="onFileError"
      >
        <uploader-unsupport></uploader-unsupport>
        <uploader-btn id="alone-uploader-btn" ref="uploadBtn" :single="true"></uploader-btn>
      </uploader>
    </div>
</template>

<script>
/**
 *  全局上传插件，两种调用方式
 *   1. 作为全局页面的组件，使用event bus
 *   调用方法：Bus.$emit('openUploader', {params: {}, options: {}})
 *               params: 发送给服务器的额外参数；
 *               options：上传选项，目前支持 target、testChunks、mergeFn、accept
 *
 *   监听函数：Bus.$on('fileAdded', fn); 文件选择后的回调
 *           Bus.$on('fileSuccess', fn); 文件上传成功的回调，监听后记得释放
 *
 *   2. 作为普通组件在单个页面中调用，使用props
 */
import { ACCEPT_CONFIG } from "./js/config";
import Bus from "../bus";
import SparkMD5 from "spark-md5";
export default {
  data() {
    return {
      initOptions: {
        target: (this.$API.ASSET_SERVER_URL != '/'?this.$API.ASSET_SERVER_URL+'/index.php?s=':'/')+this.$API.MEDIA_FILE_UPLOADS,
        chunkSize: "2048000",
        fileParameterName: "file",
        maxChunkRetries: 3,
        testChunks: true,
        query: (file, chunk) => {
          return {
            ...file.params
          }
        },
        checkChunkUploadedByResponse:function(chunk,message){
          let objMessage=JSON.parse(message);
          if(objMessage.code == 1){
            return true;
          }
          return false;
        }
      },
      fileStatusText: {
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "已暂停",
        waiting: "等待上传",
      },
      panelShow: false, //选择文件后，展示上传panel
      collapse: false,
      customParams: {},
      customStatus: "",
      fileTotal: 0,
      fileType:1,
      accept:[],
      editVisible:false,
      uploadId:'',
      isListUp:false
    };
  },
  mounted() {
    Bus.$on('alone-uploader-open', (isShow,param) => {
        if(isShow){
          setTimeout(()=>this.setUpLoadAttr(param),10)
        }
    });
  },
  computed: {
    // Uploader实例
    uploader() {
      return this.$refs.uploader.uploader;
    },
  },
  methods: {
    openUpload(){
      let input = document.querySelector("#alone-uploader-btn input");
      input.click();
    },
    setUpLoadAttr(param){
      if(param){
        // 确定文件类型
        const input = document.querySelector("#alone-uploader-btn input");
        this.accept = param.type == 0?ACCEPT_CONFIG.image:ACCEPT_CONFIG.video;
        input.setAttribute("accept", this.accept.join());
        this.customParams={
          media_id:param.media_id,
          media_file_id:param.media_file_id || '',
          type:param.type
        }
        this.uploadId=param.uploadId;
        this.isListUp=param.isListUp === true?true:false;
        this.openUpload();
      }
    },
    onFileAdded(file) {
      file.params = this.customParams;
      file.id=this.uploadId;
      if(this.isListUp === false){
        Bus.$emit("global-close-open", true);
      }
      setTimeout(()=>{
       this.computeMD5(file).then((result) => this.startUpload(result))
      },500)
    },

    /**
     * 计算md5值，以实现断点续传及秒传
     * @param file
     * @returns Promise
     */
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      let fileType={type:'md5',msg:'md5计算中..',progress:0,typeParmes:{}}
      // 文件状态设为"计算MD5"
      this.statusSet(file.id, fileType);
      file.pause();
      loadNext();
      return new Promise((resolve, reject) => {
        fileReader.onload = (e) => {
          spark.append(e.target.result);
          if (currentChunk < chunks) {
            currentChunk++;
            loadNext();
            // 实时展示MD5的计算进度
            fileType.progress=((currentChunk / chunks) * 100).toFixed(0);
            this.statusSet(file.id, fileType);
          } else {
            let md5 = spark.end();
            // md5计算完毕
            resolve({ md5, file });
            fileType={type:'md5',progress:100,msg:'md5计算完成',typeParmes:{md5,chunks,size:file.size,time:(new Date().getTime() - time)+'ms'}}
            this.statusSet(file.id, fileType);
            // console.log(
            //   `MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${
            //     file.size
            //   } 用时： ms`
            // );
          }
        };

        fileReader.onerror = function () {
          fileType.type='error';
          fileType.msg='读取出错';
          this.statusSet(file.id, fileType);
          // this.error(`文件${file.name}读取出错，请检查该文件`);
          file.cancel();
          reject();
        };
      });

      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },

    // md5计算完毕，开始上传
    startUpload({ md5, file }) {
      file.uniqueIdentifier = md5;
      file.resume();
    },

    onFileSuccess(rootFile, file, response, chunk) {
      let res = JSON.parse(response),fileType={type:'transcoding',msg:'切片合并中',progress:100,typeParmes:{}};
      if(res.code == 1){
        if(res.merge === false){
          fileType.type='success';
          fileType.msg='上传成功';
          this.statusSet(file.id, fileType);
        }else{
          this.statusSet(file.id, fileType);
          this.$http.post(this.$API.MEDIA_FILE_MERGE,{
            media_id: file.params.media_id,
            identifier: file.uniqueIdentifier,
            filename: file.name,
            totalSize: file.size,
            totalChunks: chunk.offset + 1
          }).then(result=>{
            if(result.code == 1){
              fileType.type='success';
              fileType.msg='上传成功';
            }else{
              fileType.type='error';
              fileType.msg='切片合并失败';
            }
            this.statusSet(file.id, fileType);
          }).catch(err=>{
            fileType.type='error';
            fileType.msg='切片合并失败';
            this.statusSet(file.id, fileType);
          })
        }
      }else{
        fileType.type='error';
        fileType.msg='上传失败';
        fileType.progress=0;
        this.cancelUpload();
        this.statusSet(file.id, fileType);
      }
    },

    onFileProgress(rootFile, file, chunk) {
      if(chunk && chunk.processedState && chunk.processedState.res){
        let chunkObj=null;
        try{
          chunkObj=JSON.parse(chunk.processedState.res)
        }catch(e){}
        if(chunkObj && chunkObj.code == 1){
          const progress=((chunk.endByte/file.size)*100).toFixed(0);
          let fileType={type:'start',msg:'上传中',progress:progress,currentSpeed:file.currentSpeed,typeParmes:{}}
          this.statusSet(file.id,fileType)
        }
      }
    },

    onFileError(rootFile, file, response, chunk) {
     const fileType={type:'error',msg:'上传失败',progress:0,typeParmes:{}};
      this.statusSet(file.id, fileType);
    },

    mergeFn(data){
      return this.$http.post(this.$API.a,data)
    },
    //取消上传
    cancelUpload() {
        this.thirdDialog = false;
        this.uploader.clearcache();
      },
    close() {
      this.uploader.cancel();
      this.panelShow = false;
    },
    statusSet(id,status){
      if(this.isListUp === false){
        Bus.$emit("global-uploader-change", this.uploadId,status);
      }else{
        Bus.$emit("log-uploader-change", this.uploadId,status);
      }
    },
    oncomplete(){
      console.log('文件上传结束')
    },
    /**
     * 新增的自定义的状态: 'md5'、'merging'、'transcoding'、'failed'
     * @param id
     * @param status
     */
    statusSet111(id, status) {
      let statusMap = {
        md5: {
          text: "校验MD5",
          bgc: "#fff",
        },
        merging: {
          text: "合并中",
          bgc: "#e2eeff",
        },
        transcoding: {
          text: "转码中",
          bgc: "#e2eeff",
        },
        failed: {
          text: "上传失败",
          bgc: "#e2eeff",
        },
      };

      this.customStatus = status;
      this.$nextTick(() => {
        const statusTag = document.createElement("p");
        statusTag.className = `custom-status-${id} custom-status`;
        statusTag.innerText = statusMap[status].text;
        statusTag.style.backgroundColor = statusMap[status].bgc;

        const statusWrap = document.querySelector(
          `.file_${id} .uploader-file-status`
        );
        statusWrap.appendChild(statusTag);
      });
    },

    statusRemove(id) {
      this.customStatus = "";
      this.$nextTick(() => {
        const statusTag = document.querySelector(`.custom-status-${id}`);
        statusTag.remove();
      });
    },

    emit(e) {
      Bus.$emit(e);
      this.$emit(e);
    },

    error(msg) {
      this.$notify({
        title: "错误",
        message: msg,
        type: "error",
        duration: 2000,
      });
    },
  },
};
</script>

<style lang="less">
#alone-global-uploader{
  position: absolute;
  top:0;
  left: 0;
  z-index: -1;
  width: 0;
  height: 0;
  overflow: hidden;
}
</style>
